; Compile command: nasm -f bin ./boot.asm -o ./boot.bin
; Emulator: qemu-system-x86_64 -hda ./boot.bin

ORG 0x7c00 ; run from this address 
BITS 16 ; tell asm that we use 16-BITS

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

_start:
    jmp short start
    nop

; times 33 db 0 ; BIOS parameter block - AFTER IMPLEMENTATION OF FAT16 BOOT SECTION THIS IS OBSOLETE

; FAT16 Header
OEMIdentifier           db 'TESTOS  '    ; should be 8 bytes
BytesPerSector          dw 0x200
SectorsPerCluster       db 0x80
ReservedSectors         dw 200
FATCopies               db 0x02
RootDirEntries          dw 0x40
NumSectors              dw 0x00
MediaType               db 0xF8
SectorsPerFat           dw 0x100
SectorsPerTrack         dw 0x20
NumberOfHeads           dw 0x40
HiddenSectors           dd 0x00
SectorsBig              dd 0x773594

; Extended BPB (Dos 4.0)
DriveNumber             db 0x80
WinNTBit                db 0x00
Signature               db 0x29
VolumeID                dd 0xD105
VolumeIDString          db 'TESTOS BOOT' ; should be 11 bytes
SystemIDString          db 'FAT16   '    ; should be 8 bytes

start:
    jmp 0:start2

start2:
    cli ; clear interrupts
    mov ax, 0x00
    mov ds, ax ; address of data segment = 0x7c0
    mov es, ax ; address of extra segment = 0x7c0
    mov ss, ax
    mov sp, 0x7c00

    sti ; enables interrupts 

.load_protected:
    cli 
    lgdt[gdt_descriptor] ; Load Global Descriptor Table
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax  
    jmp CODE_SEG:load32 ; call infinite loop

; GDT Table

gdt_start:
gdt_null:
    dd 0x0
    dd 0x0

; offset 0x8
gdt_code:        ; CS should point to this 
    dw 0xffff    ; Segment limit first 0-15 bits
    dw 0x0       ; Base 0-15 bits
    db 0x0       ; Base 16-23 bits 
    db 0x9a      ; Access byte
    db 11001111b ; 4 bit limit 16-19 and 4 bit flags
    db 0         ; Base 24-31

; offset 0x10 
gdt_data:
    dw 0xffff    ; Segment limit first 0-15 bits
    dw 0x0       ; Base 0-15 bits
    db 0x0       ; Base 16-23 bits 
    db 0x92      ; Access byte
    db 11001111b ; 4 bit limit 16-19 and 4 bit flags
    db 0         ; Base 24-31

gdt_end:
gdt_descriptor: 
    dw gdt_end - gdt_start-1
    dd gdt_start


; Loads the kernel into memory
[BITS 32]
load32: 
    mov eax, 1             ; static sector that we are load from (sector 1 because 0 is bootloader)
    mov ecx, 100           ; number of sectors we want to load
    mov edi, 0x0100000     ; address we want to load the kernel into - 1 Mbyte
    call ata_lba_read      ; loads into memory
    jmp CODE_SEG:0x0100000 ; jump to the kernel

; ATA driver (TODO search an info about ATA in osdev)
ata_lba_read:
    mov ebx, eax, ; Backup the LBA
    ; Send the highest 8 bits of the lba to hard disk controller
    shr eax, 24
    or eax, 0xE0 ; Select the  master drive
    mov dx, 0x1F6
    out dx, al
    ; Finished sending the highest 8 bits of the lba

    ; Send the total sectors to read
    mov eax, ecx
    mov dx, 0x1F2
    out dx, al
    ; Finished sending the total sectors to read

    ; Send more bits of the LBA
    mov eax, ebx ; Restore the backup LBA
    mov dx, 0x1F3
    out dx, al
    ; Finished sending more bits of the LBA

    ; Send more bits of the LBA
    mov dx, 0x1F4
    mov eax, ebx ; Restore the backup LBA
    shr eax, 8
    out dx, al
    ; Finished sending more bits of the LBA

    ; Send upper 16 bits of the LBA
    mov dx, 0x1F5
    mov eax, ebx ; Restore the backup LBA
    shr eax, 16
    out dx, al
    ; Finished sending upper 16 bits of the LBA

    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

    ; Read all sectors into memory
.next_sector:
    push ecx

; Checking if we need to read
.try_again:
    mov dx, 0x1f7
    in al, dx
    test al, 8
    jz .try_again

; We need to read 256 words at a time
    mov ecx, 256
    mov dx, 0x1F0
    rep insw
    pop ecx
    loop .next_sector
    ; End of reading sectors into memory
    ret

; fill whole sector with zeros...
;   $  - address of current line 
;   $$ - address of start of current sector
times 510-($ - $$) db 0 

; ... with an exception of two last bytes 0x55AA. 
; Value is 0xAA55 because of little endian format
dw 0xAA55 